using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using NWT;
using System.IO;
using System.Security.Cryptography;
using System.Xml.Serialization;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;

namespace NWT
{
	/// <summary>
	/// Main application window
	/// </summary>
	public partial class MainWindow : Form
	{

		#region Tray icon
		/// <summary>
		/// Represents tray icon
		/// </summary>
		public class NotifyIcon
		{
			/// <summary>
			/// Declare click event
			/// </summary>
			public event System.EventHandler Click;

			private WindowSink windowSink;
			private int uID = 5000;

			/// <summary>
			/// Constructor
			/// </summary>
			public NotifyIcon()
			{
				//Create instance of the MessageWindow subclass
				windowSink = new WindowSink(this);
				windowSink.uID = uID;
			}

			/// <summary>
			/// Destructor
			/// </summary>
			~NotifyIcon()
			{
				Remove();
			}

			/// <summary>
			/// Add icon to tray
			/// </summary>
			/// <param name="hIcon"></param>
			public void Add(IntPtr hIcon)
			{
				TrayMessage(windowSink.Hwnd, NIM_ADD, (uint)uID, hIcon);
			}

			/// <summary>
			/// Remove icon from tray
			/// </summary>
			public void Remove()
			{
				TrayMessage(windowSink.Hwnd, NIM_DELETE, (uint)uID, IntPtr.Zero);
			}

			/// <summary>
			/// Modify icon
			/// </summary>
			/// <param name="hIcon"></param>
			public void Modify(IntPtr hIcon)
			{
				TrayMessage(windowSink.Hwnd, NIM_MODIFY, (uint)uID, hIcon);
			}

			/// <summary>
			/// Send message to tray window
			/// </summary>
			/// <param name="hwnd"></param>
			/// <param name="dwMessage"></param>
			/// <param name="uID"></param>
			/// <param name="hIcon"></param>
			private void TrayMessage(IntPtr hwnd, int dwMessage, uint uID, IntPtr hIcon)
			{
				NOTIFYICONDATA notdata = new NOTIFYICONDATA();

				notdata.cbSize = 152;
				notdata.hIcon = hIcon;
				notdata.hWnd = hwnd;
				notdata.uCallbackMessage = WM_NOTIFY_TRAY;
				notdata.uFlags = NIF_MESSAGE | NIF_ICON;
				notdata.uID = uID;

				int ret = Shell_NotifyIcon(dwMessage, ref notdata);
			}

			#region API Declarations

			internal const int WM_LBUTTONDOWN = 0x0201;
			/// <summary>
			/// User defined message
			/// </summary>
			internal const int WM_NOTIFY_TRAY = 0x0400 + 2001;

			internal const int NIM_ADD = 0x00000000;
			internal const int NIM_MODIFY = 0x00000001;
			internal const int NIM_DELETE = 0x00000002;

			const int NIF_MESSAGE = 0x00000001;
			const int NIF_ICON = 0x00000002;

			/// <summary>
			/// NOTIFYICONDATA
			/// </summary>
			internal struct NOTIFYICONDATA
			{
				internal int cbSize;
				internal IntPtr hWnd;
				internal uint uID;
				internal uint uFlags;
				internal uint uCallbackMessage;
				internal IntPtr hIcon;
				//internal char[] szTip = new char[64]; 
				//internal IntPtr szTip;
			}

			[DllImport("coredll.dll")]
			internal static extern int Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA pnid);

			[DllImport("coredll.dll")]
			internal static extern int SetForegroundWindow(IntPtr hWnd);

			[DllImport("coredll.dll")]
			internal static extern int ShowWindow(IntPtr hWnd, int nCmdShow);

			[DllImport("coredll.dll")]
			internal static extern IntPtr GetFocus();

			#endregion

			#region WindowSink

			/// <summary>
			/// Represents window to communicate with tray
			/// </summary>
			internal class WindowSink : Microsoft.WindowsCE.Forms.MessageWindow
			{
				//Private members
				private int m_uID = 0;
				private NotifyIcon notifyIcon;

				/// <summary>
				/// Constructor
				/// </summary>
				/// <param name="notIcon"></param>
				public WindowSink(NotifyIcon notIcon)
				{
					notifyIcon = notIcon;
				}

				/// <summary>
				/// ID
				/// </summary>
				public int uID
				{
					set
					{
						m_uID = value;

					}
				}

				/// <summary>
				/// Window proc
				/// </summary>
				/// <param name="msg"></param>
				protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message msg)
				{
					if (msg.Msg == WM_NOTIFY_TRAY)
					{
						if ((int)msg.LParam == WM_LBUTTONDOWN)
						{
							if ((int)msg.WParam == m_uID)
							{
								//If somebody hooked, raise the event
								if (notifyIcon.Click != null)
									notifyIcon.Click(notifyIcon, null);

							}
						}
					}
				}
			}
			#endregion
		}
		#endregion

		#region Backlight
		public class Backlight
		{
			/// <summary>
			/// ensure the power requirement is released
			/// </summary>
			~Backlight()
			{
				Release();
			}

			/// <summary>
			/// handle to the power requirement
			/// </summary>
			private System.IntPtr handle;

			/// <summary>
			/// PowerState
			/// </summary>
			public enum PowerState
			{
				PwrDeviceUnspecified = -1,
				FULL = 0, //full on
				LOW = 1, //low power
				STANDBY = 2, //standby
				SLEEP = 3, //sleep
				OFF = 4, //off
				PwrDeviceMaximum = 5
			}

			/// <summary>
			/// set backlight level
			/// </summary>
			public void SetLevel(PowerState state)
			{
				//Release();
				//handle = SetPowerRequirement("BKL1:", state, 1, System.IntPtr.Zero, 0);
			}

			/// <summary>
			/// release power requirement
			/// </summary>
			public void Release()
			{
				if (handle.ToInt32() != 0)
				{
					int result;
					result = ReleasePowerRequirement(handle);
					handle = System.IntPtr.Zero;
				}
			}

			[DllImportAttribute("CoreDll.dll")]
			private static extern System.IntPtr SetPowerRequirement(string pvDevice, PowerState DeviceState, int DeviceFlags, System.IntPtr pvSystemState, int StateFlags);

			[DllImportAttribute("CoreDll.dll")]
			private static extern int ReleasePowerRequirement(System.IntPtr hPowerReq);
		}
		#endregion

		#region Bluetooth
		/// <summary>
		/// Bluetooth connection state
		/// </summary>
		enum BluetoothState
		{
			Unknown,
			Connecting,
			Connected,
			Error
		}
		/// <summary>
		/// Notification prototype for processing bt status changing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="status"></param>
		private delegate void BluetoothStateEventHandleDelegate(object sender, BluetoothState status);

		/// <summary>
		/// Handle bt status change
		/// </summary>
		private event BluetoothStateEventHandleDelegate OnBluetoothStateChanged;
		#endregion

		/// <summary>
		/// Tray icon
		/// </summary>
		private Icon trayIcon = null;
		/// <summary>
		/// Notify Icon
		/// </summary>
		private NotifyIcon notifyIcon;

		/// <summary>
		/// Bluetooth connection state
		/// </summary>
		private BluetoothState bluetoothState = BluetoothState.Unknown;

		/// <summary>
		/// Handle baklight power
		/// </summary>
		private Backlight backlight = new Backlight();

		/// <summary>
		/// Manager to work with GPS deviace and send data to server
		/// </summary>
		protected GPSManager gpsManager = null;

		/// <summary>
		/// Last received GPS point
		/// </summary>
		protected GPSPoint lastGPSPoint = null;

		/// <summary>
		/// Configuration settings
		/// </summary>
		protected Configuration configuration = new Configuration();

		/// <summary>
		/// Last sent point time
		/// </summary>
		protected DateTime lastSentPointTime = DateTime.MinValue;

		/// <summary>
		/// Initial form heigh
		/// </summary>
		protected int initialHeight = 0;

		/// <summary>
		/// Bluetooth on-off
		/// </summary>
		protected Bluetooth bluetooth = new Bluetooth();

		/// <summary>
		/// Constructor
		/// </summary>
		public MainWindow()
		{
			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledExceptionEvent);

			this.
			InitializeComponent();
			gpsManager = new GPSManager();
			gpsManager.OnGPSDetectStatus += new GPSDetectStatusEventHandleDelegate(gpsManager_OnGPSDetectStatus);
			gpsManager.OnGPSPointReceived += new GPSPointReceivedDelegate(gpsManager_OnGPSPointReceived);
			gpsManager.OnErrorCallBack += new ErrorDelegate(gpsManager_OnErrorCallBack);
			gpsManager.OnNetworkStatusChanged += new NetworkStatusChangedDelegate(gpsManager_OnNetworkStatusChanged);
			gpsManager.OnStatusChanged += new EventHandler(gpsManager_OnStatusChanged);
			OnBluetoothStateChanged += new BluetoothStateEventHandleDelegate(MainWindow_OnBluetoothStateChanged);

			// attach to traffic counter
			GPSManager.NetworkTrafficCounter.AmountChanged += new EventHandler(NetworkTrafficCounter_AmountChanged);

			// setup tray icon
			trayIcon = Properties.Resources.icon2;
			notifyIcon = new NotifyIcon();
			notifyIcon.Add(trayIcon.Handle);
			notifyIcon.Click += new EventHandler(notifyIcon_Click);
		}

		/// <summary>
		/// Process BT status changing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="status"></param>
		void MainWindow_OnBluetoothStateChanged(object sender, MainWindow.BluetoothState status)
		{
			bluetoothState = status;
			switch (bluetoothState)
			{
				case BluetoothState.Connecting:
					SetStatusText("Turning bluetooth on...");
					break;
				case BluetoothState.Connected:
					SetStatusText("Bluetooth is turned on.");
					break;
				case BluetoothState.Error:
					SetStatusText("Error turning on bluetooth.");
					break;
			}
		}

		/// <summary>
		/// Process notification from tray
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void notifyIcon_Click(object sender, EventArgs e)
		{
			// activate window
			this.Activate();
		}

		/// <summary>
		/// Process traffic change value
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void NetworkTrafficCounter_AmountChanged(object sender, EventArgs e)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new EventHandler(OnAmountChanged), new object[] { sender, e });
			else
				OnAmountChanged(sender, e);
		}

		/// <summary>
		/// Process traffic change value
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void OnAmountChanged(object sender, EventArgs e)
		{
			txtTrafficAmount.Text = GPSManager.NetworkTrafficCounter.Amount.ToString();
		}

		/// <summary>
		/// Process manager status changing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void OnManagerStatusChanged(object sender, EventArgs e)
		{
			mniGo.Text = gpsManager.Status == ManagerStatus.Stopped ? "Go" : "Stop";
			mniAutodetect.Enabled = gpsManager.Status == ManagerStatus.Stopped;
			mniMonitor.Enabled = gpsManager.Status == ManagerStatus.Stopped;
			mniStop.Enabled = gpsManager.Status != ManagerStatus.Stopped;
			mniConfiguration.Enabled = gpsManager.Status == ManagerStatus.Stopped;

			// reset UI on stop
			if (gpsManager.Status == ManagerStatus.Stopped)
			{
				lblSentStatus.BackColor = Color.Black;
				lblSentStatus.ForeColor = Color.PaleGreen;

				txtLatitude.BackColor = Color.Black;
				txtLongitude.BackColor = Color.Black;
				txtSpeed.BackColor = Color.Black;

				txtLatitude.ForeColor = Color.PaleGreen;
				txtLongitude.ForeColor = Color.PaleGreen;
				txtSpeed.ForeColor = Color.PaleGreen;
			}
		}

		/// <summary>
		/// Process status changing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void gpsManager_OnStatusChanged(object sender, EventArgs e)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new EventHandler(OnManagerStatusChanged), new object[] { sender, e });
			else
				OnManagerStatusChanged(sender, e);
		}

		/// <summary>
		/// Notify of detection GPS parameters
		/// </summary>
		/// <param name="status"></param>
		/// <param name="port"></param>
		/// <param name="baudRate"></param>
		/// <param name="e"></param>
		protected void gpsManager_OnGPSDetectStatus(object sender, GPSDetectStatus status, string port, int baudRate, Exception e)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new GPSDetectStatusEventHandleDelegate(OnGPSDetectStatus), new object[] { sender, status, port, baudRate, e });
			else
				OnGPSDetectStatus(sender, status, port, baudRate, e);
		}

		/// <summary>
		/// Process notification
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="status"></param>
		/// <param name="port"></param>
		/// <param name="baudRate"></param>
		/// <param name="e"></param>
		protected void OnGPSDetectStatus(object sender, GPSDetectStatus status, string port, int baudRate, Exception e)
		{
			// process autodetect status
			string text = string.Empty;
			switch (status)
			{
				case GPSDetectStatus.Started:
					text = "Autodetect GPS device started...";
					break;
				case GPSDetectStatus.Testing:
					text = string.Format("Testing port {0} at speed {1}", port, baudRate);
					break;
				case GPSDetectStatus.Tested:
					text = string.Format("Port {0} tested", port);
					break;
				case GPSDetectStatus.Finished:
					text = "Autodetect GPS device finished";
					// start monitoring
					if (go && autoDetectedGPSDeviceParameters != null)
						StartMonitoring();
					go = false;
					break;
				case GPSDetectStatus.NotFound:
					autoDetectedGPSDeviceParameters = null;
					text = "GPS device not found";
					break;
				case GPSDetectStatus.Detected:
					text = string.Format("Detected GPS device on {0} at speed {1}", port, baudRate);
					autoDetectedGPSDeviceParameters = gpsManager.AutoDetectedGPSParameters;
					this.txtGPSPort.Text = autoDetectedGPSDeviceParameters.PortName;
					break;
				case GPSDetectStatus.Error:
					text = string.Format("Error testing port {0}: {1}", port, e.Message);
					break;
			}
			SetStatusText(text);
		}		
		
		/// <summary>
		/// Update network status
		/// </summary>
		/// <param name="networkStatus"></param>
		protected void UpdateNetworkStatus(object sender, NetworkStatus networkStatus)
		{
			switch (networkStatus)
			{
				case NetworkStatus.Sending:
					lblSentStatus.BackColor = Color.PaleGreen;
					lblSentStatus.ForeColor = Color.Black;
					break;
				case NetworkStatus.Waiting:
					lblSentStatus.BackColor = Color.Black;
					lblSentStatus.ForeColor = Color.PaleGreen;
					break;
				case NetworkStatus.SentOK:
					lblSentStatus.BackColor = Color.Black;
					lblSentStatus.ForeColor = Color.PaleGreen;
					lastSentPointTime = DateTime.Now;
					UpdateSentStatus();
					break;
			}
		}

		/// <summary>
		/// Process network status
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="networkStatus"></param>
		void gpsManager_OnNetworkStatusChanged(object sender, NetworkStatus networkStatus)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new NetworkStatusChangedDelegate(UpdateNetworkStatus), new object[] { sender, networkStatus });
			else
				UpdateNetworkStatus(sender, networkStatus);
		}

		/// <summary>
		/// Process device error event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="ex"></param>
		void OnErrorCallBack(object sender, Exception ex)
		{
			string text = ex.ToString();
			SetStatusText("\r\n" + DateTime.Now.ToString() + ": " + text);

			// write to log file
			WriteLog(ex.ToString());

			// check to start autodetect
			if (go && ex is GPSDeviceNotFound)
			{
				autoDetectedGPSDeviceParameters = null;
				gpsManager.StartDetectingGPSParameters(true);
			}
		}

		/// <summary>
		/// Process device error event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="ex"></param>
		void gpsManager_OnErrorCallBack(object sender, Exception ex)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new ErrorDelegate(OnErrorCallBack), new object[] { sender, ex });
			else
				OnErrorCallBack(sender, ex);
		}
		/// <summary>
		/// Exception handler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		void OnUnhandledExceptionEvent(object sender, UnhandledExceptionEventArgs args)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new ErrorDelegate(OnErrorCallBack), new object[] { sender, (Exception)args.ExceptionObject});
			else
				OnErrorCallBack(sender, (Exception)args.ExceptionObject);
		}

		/// <summary>
		/// Update UI elements
		/// </summary>
		/// <param name="gpsPoint"></param>
		protected void UpdatePointPosition(object sender, GPSPoint gpsPoint)
		{
			// prevent autodetect on data arrived
			lastGPSPoint = gpsPoint;
			go = false;

			// write to log
			float latitude = gpsPoint.latitude ?? default(float);
			float longitude = gpsPoint.longitude ?? default(float);
			float speed = gpsPoint.speed ?? default(float);
			float altitude = gpsPoint.altitude ?? default(float);
			if (chkLog.Checked)
			{
				string text = string.Format("{4}{0}:{1}, {2}, h:{3}", 
					latitude.ToString("0.#####").Replace(",", "."), 
					longitude.ToString("0.#####").Replace(",", "."), 
					speed.ToString("0.##").Replace(",", "."),
					altitude.ToString("0.##").Replace(",", "."), 
					object.Equals(gpsPoint.fixObtained, true) ? string.Empty : "*");
				SetStatusText(text);
			}

			// set control text
			txtLatitude.Text = latitude.ToString("0.#####").Replace(",", ".");
			txtLongitude.Text = longitude.ToString("0.#####").Replace(",", ".");
			txtSpeed.Text = speed.ToString("0.##").Replace(",", ".");

			// update colors
			if (gpsPoint.fixObtained.HasValue && gpsPoint.fixObtained.Value)
			{
				txtLatitude.BackColor = Color.Black;
				txtLongitude.BackColor = Color.Black;
				txtSpeed.BackColor = Color.Black;

				txtLatitude.ForeColor = Color.PaleGreen;
				txtLongitude.ForeColor = Color.PaleGreen;
				txtSpeed.ForeColor = Color.PaleGreen;
			}
			else
			{
				txtLatitude.BackColor = Color.PaleGreen;
				txtLongitude.BackColor = Color.PaleGreen;
				txtSpeed.BackColor = Color.PaleGreen;

				txtLatitude.ForeColor = Color.Black;
				txtLongitude.ForeColor = Color.Black;
				txtSpeed.ForeColor = Color.Black;
			}
		}

		/// <summary>
		/// Process new point 
		/// </summary>
		/// <param name="gpsManager"></param>
		/// <param name="gpsPoint"></param>
		void gpsManager_OnGPSPointReceived(object sender, GPSPoint gpsPoint)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new GPSPointReceivedDelegate(UpdatePointPosition), new object[] { sender, gpsPoint });
			else
				UpdatePointPosition(sender, gpsPoint);
		}

		#region Auto Detect parameters

		/// <summary>
		/// Show status text
		/// </summary>
		/// <param name="status"></param>
		protected void SetStatusText(string status)
		{
			if (this.InvokeRequired)
				this.BeginInvoke(new StringEventHandleDelegate(SetStatusTextInvoke), new object[] { this, status });
			else
				SetStatusTextInvoke(this, status);
		}

		/// <summary>
		/// Show status text
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="status"></param>
		private void SetStatusTextInvoke(object sender, string status)
		{
			txtStatusWindow.SelectionStart = txtStatusWindow.Text.Length;
			txtStatusWindow.SelectionLength = 0;
			txtStatusWindow.SelectedText = status + "\r\n";
			txtStatusWindow.ScrollToCaret();
		}

		/// <summary>
		/// Autoprocessing GPS params
		/// </summary>
		protected bool go = false;

		/// <summary>
		/// Autodetected GPS device parameter
		/// </summary>
		protected GPSDeviceParameters autoDetectedGPSDeviceParameters = null;

		#endregion

		/// <summary>
		/// Start detect GPS device
		/// </summary>
		protected void StartAutoDetect()
		{
			autoDetectedGPSDeviceParameters = null;
			gpsManager.StartDetectingGPSParameters(true);
		}

		/// <summary>
		/// Start monitoring process
		/// </summary>
		protected void StartMonitoring()
		{
			if (autoDetectedGPSDeviceParameters != null)
				configuration.DeviceParameters = autoDetectedGPSDeviceParameters;

			// update port setting
			if (this.txtGPSPort.Text != configuration.DeviceParameters.PortName)
			{
				configuration.DeviceParameters.PortName = this.txtGPSPort.Text;
				configuration.Modified = true;
			}

			// start
			gpsManager.StartProcessData(configuration);

			// Show info text
			string info = string.Empty;
			info += "\r\n" + "Name:\t\t" + configuration.NetworkParameters.MarkerName;
			info += "\r\n" + "Game:\t\t" + configuration.NetworkParameters.GameName;
			info += "\r\n" + "User:\t\t" + configuration.NetworkParameters.UserLogin;
			info += "\r\n" + "Pause:\t\t" + configuration.NetworkParameters.SendingPause + " ms";
			info += "\r\n" + "MaxPause:\t" + configuration.NetworkParameters.MaxSendingPause + " ms";
			info += "\r\n" + "Distance:\t\t" + configuration.NetworkParameters.DistanceInMeters + " m";
			info += "\r\n";
			SetStatusText(info);

			// allow backlight to switch off
			backlight.SetLevel(Backlight.PowerState.STANDBY);
		}

		/// <summary>
		/// Exit application
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mnuExit_Click(object sender, EventArgs e)
		{
			this.Close();
			//Application.Exit();
		}

		/// <summary>
		/// Auto find GPS and process GPS data
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniGo_Click(object sender, EventArgs e)
		{
			if (gpsManager.Status == ManagerStatus.Stopped)
			{
				go = true;
				StartMonitoring();
			}
			else
			{
				go = false;
				gpsManager.Stop();
			}
		}

		/// <summary>
		/// Autodetect params of GPS device
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniAutodetect_Click(object sender, EventArgs e)
		{
			autoDetectedGPSDeviceParameters = null;
			gpsManager.StartDetectingGPSParameters(true);
		}

		/// <summary>
		/// Monitor GPS data
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniMonitor_Click(object sender, EventArgs e)
		{
			go = false;
			StartMonitoring();
		}

		/// <summary>
		/// Stop processing and detecting
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniStop_Click(object sender, EventArgs e)
		{
			go = false;

			// reset backlight status
			backlight.Release();

			gpsManager.Stop();
		}

		/// <summary>
		/// Clear text from status window
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniClear_Click(object sender, EventArgs e)
		{
			txtStatusWindow.Text = string.Empty;
		}

		/// <summary>
		/// Clear text from status window
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnClear_Click(object sender, EventArgs e)
		{
			txtStatusWindow.Text = string.Empty;
		}
		
		/// <summary>
		/// Class for sending data
		/// </summary>
		private class SendParam
		{
			public string Name = string.Empty;
			public string Address = string.Empty;
			public string Subject = string.Empty;
			public string Body = string.Empty;
		}

		/// <summary>
		/// Send SMS
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mnuSendSMS_Click(object sender, EventArgs e)
		{
			try
			{
				if (configuration.SMSName == string.Empty || configuration.SMSPhone == string.Empty)
				{
					string text = "Use configuration to setup SMS name and phone number";
					SetStatusText(text);
					// write to log file
					WriteLog(text);
				}
				else
				{
					if (lastGPSPoint == null || lastGPSPoint.fixObtained == false)
					{
						string text = "Position unavailable";
						SetStatusText(text);
						// write to log file
						WriteLog(text);
					}
					else
					{
						//Set the message body and recipient
						string text = string.Empty;
						float latitude = lastGPSPoint.latitude ?? default(float);
						float longitude = lastGPSPoint.longitude ?? default(float);
						float speed = lastGPSPoint.speed ?? default(float);
						float altitude = lastGPSPoint.altitude ?? default(float);

						// set control text
						text += latitude.ToString("0.#####").Replace(",", ".");
						text += "," + longitude.ToString("0.#####").Replace(",", ".");
						text += "\r\nspeed:" + speed.ToString("0.##").Replace(",", ".");
						text += "\r\nalt:" + altitude.ToString("0.#####").Replace(",", ".");
						text += "\r\n" + configuration.NetworkParameters.MarkerName;
						text += "," + configuration.NetworkParameters.GameName;

						SendParam sp = new SendParam();
						sp.Name = configuration.SMSName;
						sp.Address = configuration.SMSPhone;
						sp.Body = text;

						// Queue sending
						ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProcSendSMS), sp);
					}
				}
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}
		}

		/// <summary>
		/// Send SMS
		/// </summary>
		/// <param name="stateInfo"></param>
		protected void ThreadProcSendSMS(Object stateInfo)
		{
			try
			{
				SetStatusText("Sending message (SMS)...");

				SendParam sp = (SendParam)stateInfo;
				SmsMessage smsMessage = new SmsMessage();
				smsMessage.Body = sp.Body;
				smsMessage.To.Add(new Recipient(sp.Name, sp.Address));
				smsMessage.RequestDeliveryReport = true;

				//Send the SMS message.
				smsMessage.Send();
				
				SetStatusText("Message sent (SMS).");
			}
			catch (Exception e)
			{
				SetStatusText("Error sending message (SMS).");
				gpsManager_OnErrorCallBack(this, e);
			}
		}

		/// <summary>
		/// Send Email
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mnuSendEmail_Click(object sender, EventArgs e)
		{
			try
			{
				if (configuration.SMSName == string.Empty || configuration.SMSPhone == string.Empty)
				{
					string text = "Use configuration to setup SMS name and phone number";
					SetStatusText(text);
					// write to log file
					WriteLog(text);
				}
				else
				{
					if (lastGPSPoint == null || lastGPSPoint.fixObtained == false)
					{
						string text = "Position unavailable";
						SetStatusText(text);
						// write to log file
						WriteLog(text);
					}
					else
					{
						//Set the message body and recipient
						string text = string.Empty;
						float latitude = lastGPSPoint.latitude ?? default(float);
						float longitude = lastGPSPoint.longitude ?? default(float);
						float speed = lastGPSPoint.speed ?? default(float);
						float altitude = lastGPSPoint.altitude ?? default(float);

						// set control text
						text += latitude.ToString("0.#####").Replace(",", ".");
						text += "," + longitude.ToString("0.#####").Replace(",", ".");
						text += "\r\nspeed:" + speed.ToString("0.##").Replace(",", ".");
						text += "\r\nalt:" + altitude.ToString("0.#####").Replace(",", ".");

						SendParam sp = new SendParam();
						sp.Name = configuration.EmailName;
						sp.Address = configuration.EmailAddress;
						sp.Subject = configuration.NetworkParameters.MarkerName + ", " + configuration.NetworkParameters.GameName;
						sp.Body = text;

						// Queue sending
						ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProcSendEMAIL), sp);
					}
				}
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}
		}

		/// <summary>
		/// Send EMAIL
		/// </summary>
		/// <param name="stateInfo"></param>
		protected void ThreadProcSendEMAIL(Object stateInfo)
		{
			try
			{
				SetStatusText("Sending message (Email)...");

				SendParam sp = (SendParam)stateInfo;
				EmailMessage message = new EmailMessage();
				message.Subject = sp.Subject;
				message.BodyText = sp.Body;
				message.To.Add(new Recipient(sp.Name, sp.Address));

				using (OutlookSession ses = new OutlookSession())
				{
					if (ses.EmailAccounts.Count > 0)
						ses.EmailAccounts[0].Send(message);
				}

				SetStatusText("Message sent (Email).");
			}
			catch (Exception e)
			{
				SetStatusText("Error sending message (Email).");
				gpsManager_OnErrorCallBack(this, e);
			}
		}

		/// <summary>
		/// Prform setting changing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mniConfiguration_Click(object sender, EventArgs e)
		{
			try
			{
				SettingsWindow sw = new SettingsWindow(configuration);
				if (sw.ShowDialog() == DialogResult.OK)
				{
					configuration.Modified = true;
					// update UI
					OnSettingsChanged();
					// save settings
					configuration.SaveConfiguration(this.ConfigFileName);
					configuration.Modified = false;
				}
				sw.Dispose();
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}
		}

		/// <summary>
		/// Application path
		/// </summary>
		protected string applicationPath = string.Empty;
		/// <summary>
		/// Application path
		/// </summary>
		protected string ApplicationPath
		{
			get
			{
				if (applicationPath == string.Empty)
				{
					string path = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase;
					if (path.StartsWith("file:///"))
						applicationPath = path.Substring("file:///".Length);
					else
						applicationPath = path;
				}
				return applicationPath;
			}
		}

		/// <summary>
		/// Log file name
		/// </summary>
		protected string LogFileName
		{
			get
			{
				return Path.Combine(Path.GetDirectoryName(this.ApplicationPath), "NWT_PPC_log.log");
			}
		}

		/// <summary>
		/// Config file name
		/// </summary>
		protected string ConfigFileName
		{
			get
			{
				return Path.Combine(Path.GetDirectoryName(this.ApplicationPath), "NWT_PPC_config.config");
			}
		}

		/// <summary>
		/// Write text to log
		/// </summary>
		/// <param name="text"></param>
		protected void WriteLog(string text)
		{
			try
			{
				if (configuration.LogToFile)
					FileLogger.Write(this.LogFileName, text);
			}
			catch
			{
			}
		}

		/// <summary>
		/// Perform form loading
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void MainWindow_Load(object sender, EventArgs e)
		{
			initialHeight = pnlBackground.Height;

			try
			{
				// write to log file
				WriteLog("Program started");

				// load settings
				configuration.LoadConfiguration(this.ConfigFileName);
				GPSManager.NetworkTrafficCounter.Amount = configuration.TrafficAmount;
			}
			catch (System.IO.FileNotFoundException)
			{
				// config file not found
				OnErrorCallBack(this, new ApplicationException(string.Format("Configuration file {0} not found.", this.ConfigFileName)));
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}

			// compute password
			if (configuration.UseMD5ForPassword)
			{
				MD5 md5 = new MD5CryptoServiceProvider();
				byte[] password = Encoding.UTF8.GetBytes(configuration.NetworkParameters.UserPassword);
				byte[] hashedPassword = md5.ComputeHash(password);
				StringBuilder sb = new StringBuilder();
				foreach (byte b1 in hashedPassword)
					sb.Append(b1.ToString("x"));
				configuration.NetworkParameters.UserPassword = sb.ToString();
			}

			// turn on bluetooth
			try
			{
				if (configuration.TurnBluetoothOn)
				{
					try
					{
						ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProcTurnBluetoothOn));
						//Cursor.Current = Cursors.WaitCursor;
					}
					finally
					{
						//Cursor.Current = Cursors.Default;
					}
				}
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}

			// update UI
			OnSettingsChanged();
		}

		/// <summary>
		/// Turn bluetooth on
		/// </summary>
		/// <param name="stateInfo"></param>
		protected void ThreadProcTurnBluetoothOn(Object stateInfo)
		{
			try
			{
				// inform
				if (OnBluetoothStateChanged != null)
					OnBluetoothStateChanged(this, BluetoothState.Connecting);

				// on
				bluetooth.Init();

				// inform
				if (OnBluetoothStateChanged != null)
					OnBluetoothStateChanged(this, BluetoothState.Connected);
			}
			catch (Exception e)
			{
				// inform
				if (OnBluetoothStateChanged != null)
					OnBluetoothStateChanged(this, BluetoothState.Error);
				gpsManager_OnErrorCallBack(this, e);
			}
		}


		/// <summary>
		/// Perform settings changed
		/// </summary>
		protected void OnSettingsChanged()
		{
			// set com port
			this.txtGPSPort.Visible = configuration.GPSAccess == 1;
			this.txtGPSPort.Text = configuration.DeviceParameters.PortName;
			gpsManager.GPSAccessType = configuration.GPSAccess;
			gpsManager.NetworkSendingMethod = configuration.NetworkSendingMethod;

			// process toolbar panel
			pnlToolbar.Visible = configuration.SMSShowButton || configuration.EmailShowButton;
			pnlSMSButton.Visible = configuration.SMSShowButton;
			pnlEmailButton.Visible = configuration.EmailShowButton;
			pnlTraffic.Visible = configuration.TrafficCalculate;
		}

		/// <summary>
		/// Process panel event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void inputPanel_EnabledChanged(object sender, EventArgs e)
		{
			if (inputPanel != null)
			{
				Microsoft.WindowsCE.Forms.InputPanel ip = (Microsoft.WindowsCE.Forms.InputPanel)inputPanel;
				if (ip.Enabled)
				{
					Size pnlLogSize = pnlLog.Size;
					pnlLog.Dock = DockStyle.None;
					pnlLog.Size = new Size(pnlLogSize.Width, pnlLogSize.Height - ip.Bounds.Height);
				}
				else
				{
					pnlLog.Dock = DockStyle.Fill;
				}
			}
		}

		/// <summary>
		/// Update sent status
		/// </summary>
		protected void UpdateSentStatus()
		{
			if (lastSentPointTime == DateTime.MinValue)
				lblSentStatus.Text = string.Empty;
			else
			{
				TimeSpan delta = DateTime.Now - lastSentPointTime;
				lblSentStatus.Text = string.Format("{0}{1}{2}{3}{4}{5}",
					delta.Hours > 0 ? delta.Hours.ToString() : string.Empty,
					delta.Hours > 0 ? "h " : string.Empty,
					delta.Minutes > 0 ? delta.Minutes.ToString() : string.Empty,
					delta.Minutes > 0 ? "m " : string.Empty,
					delta.Seconds.ToString(),
					"s");
			}
		}

		/// <summary>
		/// Update sent status
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void tmrSentStatus_Tick(object sender, EventArgs e)
		{
			UpdateSentStatus();
		}

		/// <summary>
		/// Process form closed event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void MainWindow_Closed(object sender, EventArgs e)
		{
			try
			{
				// ask to save settings
				if (!File.Exists(this.ConfigFileName)
					|| (configuration.Modified
					&& MessageBox.Show("Save settings?", "GPS Sender", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes))
				{
					// Save traffic
					if (configuration.TrafficCalculate)
						configuration.TrafficAmount = GPSManager.NetworkTrafficCounter.Amount;
					configuration.SaveConfiguration(this.ConfigFileName);
				}
				// save traffic
				else if (configuration.TrafficCalculate)
				{
					if (File.Exists(this.ConfigFileName))
					{
						// load settings
						configuration.LoadConfiguration(this.ConfigFileName);
						configuration.TrafficCalculate = true;
					}
					configuration.TrafficAmount = GPSManager.NetworkTrafficCounter.Amount;
					configuration.SaveConfiguration(this.ConfigFileName);
				}

				// remove tray icon
				notifyIcon.Remove();

				// log to file
				WriteLog("Program finished");
			}
			catch (Exception ex)
			{
				// log error
				OnErrorCallBack(this, ex);
			}
		}
	}
}
